#pragma once 
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<pthread.h>
#include<unistd.h>
using std::cout;
using std::endl;
using std::string;
using std::vector;
using std::queue;

//如果要管理一批线程就绕不开先描述后组织，我们可以使用一个动态数组管理这一批线程结构体的信息

struct ThreadInfo//线程信息结构体
{
    pthread_t tid;
    string name;
};

static const int defaultnum=5;

template<class T>
class ThreadPool//线程池类
{
public: 
    //申请锁
    void Lock()
    {
        pthread_mutex_lock(&mutex_);

    }
    void Unlock()
    {
        pthread_mutex_unlock(&mutex_);
    }

    //唤醒线程：
    void Wakeup()
    {
        pthread_cond_signal(&cond_);
    }

    //线程休眠：
    void ThreadSleep()
    {
        pthread_cond_wait(&cond_,&mutex_);
    }

    //判断任务队列是否为空
    bool IsQueueEmpty()
    {
        return tasks_.empty();
    }

    string GetThreadName(pthread_t tid)
    {
        for(const auto &e:threads_)
        {
            if(tid==e.tid)
            {
                return e.name;
            }
        }
        return "None";
    }

public:
    //线程执行的函数：由于在类内定义，但是不能让this指针占位破坏执行函数的格式，所以定义为静态成员函数
    static void *HandlerTask(void *args)
    {
        ThreadPool<T>*tp=static_cast<ThreadPool<T>*>(args);//传递的数据是整个线程池指针
        string name=tp->GetThreadName(pthread_self());
        while(true)
        {
            //申请锁：
            tp->Lock();
            //任务队列为空的时候，该线程进入等待队列
            while(tp->IsQueueEmpty())//循环判断防止误唤醒
            {
                //等待的时候会释放锁，唤醒后会重新竞争持有锁
                tp->ThreadSleep();
            }
            T t=tp->Pop();//获取数据
            //得到数据之后就可以解锁了，后面的处理数据让线程并发的处理即可
            tp->Unlock();
            t();//处理数据，这里处理数据就会把客户端输入的单词翻译后发送给客户端，再关闭对应的文件描述符
        }
    }
    
    //开始启动线程池：也就是创建一批线程
    void Start()
    {
        int num=threads_.size();
        for(int i=0;i<num;i++)
        {
            threads_[i].name="thread-"+std::to_string(i+1);

            //创建线程，给线程传递该线程池的地址，这样线程就可以使用线程池中的资源和锁了
            pthread_create(&threads_[i].tid,nullptr,HandlerTask,this);

        }
    }

    //pop任务
    T Pop()
    {
        T t=tasks_.front();
        tasks_.pop();
        return t;
    }

    void Push(const T&t)
    {
        //加载任务也要竞争锁防止任务加载和任务处理发生错乱
        Lock();
        //push任务
        tasks_.push(t);
        //唤醒一个处理任务的线程
        Wakeup();
        //解锁
        Unlock();
    }

    //静态成员函数只能访问静态的成员变，所以可以直接访问我们静态区的线程池指针tp_，从而通过tp_指针去调用线程池中的接口函数
    //我们设计这个接口函数就是为了限定以后只能通过这个函数创建唯一的线程池，也只能通过这个函数作为接口简间接的使用tp_指针去调用线程池中的所有接口函数
    static ThreadPool<T>*GetInstance()//懒汉的设计模式：只有在第一次调用该函数的地方才会实例化对象，加快了程序的启动时间
    {
        if(nullptr==tp_)//典型的单例模式实现：tp_被检查是否为nullptr。如果是，那么代码会进入一个互斥锁保护的块中，再次检查tp_是否为nullptr，这是为了防止多线程同时进入这个块并创建多个实例。如果tp_仍然是nullptr，那么就会在堆上使用new操作符创建ThreadPool的一个新实例，并将其地址赋值给tp_。这个新创建的实例不会在离开函数时被销毁，因为它的生命周期是由tp_管理的。
        {
            pthread_mutex_lock(&lock_);//使用互斥锁保护下一次的判断
            if(nullptr==tp_)//双重检查防止创建多个实例
            {
                
                tp_=new ThreadPool<T> ();//虽然构造函数私有化了，但是静态成员函数也算是类成员，是可以访问私有的成员函数的也就包括构造函数，所以可以在这个静态的接口函数中创建唯一的线程池
                cout<<"lofg:singleton create done first"<<endl;
            }
            pthread_mutex_unlock(&lock_);
        }

        //如果不为空的话就会返回创建好的线程池地址
        return tp_;//返回一个在堆区创建好的线程池的地址
    }
private:
    //构造函数的私有化，使得不能通过普通的方式创建线程池类对象，就只能使用我们提供的静态成员方法来实现单例的创建线程池
    ThreadPool(int num=defaultnum):threads_(num)//有参构造动态数组，传一个参数代表初始容量大小
    {
        pthread_mutex_init(&mutex_,nullptr);
        pthread_cond_init(&cond_,nullptr);
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }

    //它使用了C++11引入的删除语义（deleted semantics）来禁止类的拷贝构造函数和拷贝赋值操作符
    ThreadPool(const ThreadPool<T>&)=delete;//拷贝构造
    const ThreadPool<T>&operator=(const ThreadPool<T>&)=delete;//重载赋值运算符
    //解释：这种写法通常用于那些不应该被拷贝的类，比如管理资源（如线程池）的类。因为拷贝这样的类可能会导致资源管理上的混乱，比如两个线程池实例可能尝试管理同一组线程，从而导致不可预测的行为。

private: 
    vector<ThreadInfo>threads_;//线程池的底层容器，里面组织了一批创建的线程的信息
    queue<T>tasks_;//任务队列

    pthread_mutex_t mutex_;//由于所有的线程访问同一个任务队列，要由锁来保护同步互斥的访问这些临界资源 
    pthread_cond_t cond_;//条件变量

    static ThreadPool<T>*tp_;//在静态区维护一个线程池的指针（懒汉设计模式）
    static pthread_mutex_t lock_;//静态区维护一个互斥锁，用来创建线程池的时候使用
};
//静态成员变量在类外初始化：
template<class T>
ThreadPool<T>*ThreadPool<T>::tp_=nullptr;

template<class T>
pthread_mutex_t ThreadPool<T>::lock_=PTHREAD_MUTEX_INITIALIZER;//类外初始化这把锁